function isPlainObject(obj){
    let prototype;
    return Object.prototype.toString.call(obj) === '[object Object]' 
        && (prototype = Object.getPrototypeOf(obj), prototype === null || 
        prototype == Object.getPrototypeOf({}))
}

// 用于判断是否为 proxy 对象
function isProxy(value){return  !!value && !!value['MY_IMMER']}
// 存放生成的 proxy 对象
const proxies = new Map()

function getProxy(data){
  if (isProxy(data)) {
    return data
  }
  if (isPlainObject(data) || Array.isArray(data)) {
    if (proxies.has(data)) {
      return proxies.get(data)
    }
    const proxy = new Proxy(data, objectTraps)
    proxies.set(data, proxy)
    return proxy
  }
  return data
}



// 注意这里还是用到了 Map，原理和上文说的一致
const copies = new Map()
const objectTraps = {
  get(target, key) {
    if (key === 'MY_IMMER') return target
    const data = copies.get(target) || target
    return getProxy(data[key])
  },
  set(target, key, val) {
    const copy = getCopy(target)
    const newValue = getProxy(val)
    // 这里的判断用于拿 proxy 的 target
    // 否则直接 copy[key] = newValue 的话外部拿到的对象是个 proxy
    copy[key] = isProxy(newValue) ? newValue[MY_IMMER] : newValue
    return true
  }
}
const getCopy = data => {
  if (copies.has(data)) {
    return copies.get(data)
  }
  const copy = Array.isArray(data) ? data.slice() : { ...data }
  copies.set(data, copy)
  return copy
}

const isChange = data => {
    if (proxies.has(data) || copies.has(data)) return true
  }
  
  const finalize = data => {
      //判断是不是
    if (isPlainObject(data) || Array.isArray(data)) {
      if (!isChange(data)) {
        return data
      }
      const copy = getCopy(data)
      Object.keys(copy).forEach(key => {
        copy[key] = finalize(copy[key])
      })
      return copy
    }
    return data
  }





  function produce(baseState, fn) {
    // 获取代理对象
    const proxy = getProxy(baseState)
    //执行回调
    fn(proxy)
    //
    return finalize(baseState)
  }




  let sj = {name:"cmc1",obj:{
      name:"obj"
  }};

  console.log(sj)

  const data = produce(sj, draftState => {
    // draftState.obj.age = 26
  })
  
  data.obj.name="cmc2";

  console.log(sj,data)
  console.log(data.obj===sj.obj);